/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Network;


import IO.Question;
import IO.Exam;
import IO.IOFIle;
import IO.ClientAnswer;

import javax.xml.parsers.ParserConfigurationException;
import server.MainFrameServer;

import java.io.*;
//import java.net.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author trang
 */


public class NetworkServer extends NetworkConnection {
    
    private int numConnection;
    private int port;
    ServerSocket serverListener;
    Socket incomingConnection = new Socket();
    private List<Socket> clients = null;
    private List<ConnectionHandler> threads = new ArrayList<ConnectionHandler>();

    public boolean isConnect = false;
    public boolean allowConnect = false;
    MainFrameServer frameServer = null;
    
    public IO.Exam exam;

    
    public IO.Exam loadExam(String filename)
    {
        try {
            exam = new IO.Exam(filename);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(NetworkServer.class.getName()).log(Level.SEVERE, null, ex);
        }
        return exam;
    }
    
    public NetworkServer(int port) {        
        super();
        this.port = port;
        numConnection = 5;
        clients = new ArrayList<Socket>();
        try {
            serverListener = new ServerSocket(port);
            

//            serverListener = new ServerSocket(port, getNumConnection(), InetAddress.getLocalHost());
        } catch (IOException ex) {
            Logger.getLogger(NetworkServer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public NetworkServer() {
        super();
        numConnection = 5;
        clients = new ArrayList<Socket>();
        try {
            serverListener = new ServerSocket(port);
            
//            serverListener = new ServerSocket( getPort(), getNumConnection(), InetAddress.getLocalHost());
            
        } catch (IOException ex) {
            Logger.getLogger(NetworkServer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void serverAccept(int port)
    {
        try {
            if(serverListener!= null)
            {            
                serverListener.close();

            }
            serverListener = new ServerSocket(port);
        } catch (IOException ex) {
                Logger.getLogger(NetworkServer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     *
     * @return
     */

    public boolean  checkConnection()
    {
        return true;
    }

    /**
     * open Connection for listening with specific port
     * @param port: specific config port for listenning
     * @return
     */

    public boolean openConnection(int port,MainFrameServer frame)
    {
        this.frameServer = frame;
        allowConnect = true;
        while(allowConnect)
        {            
            try {
                incomingConnection = serverListener.accept();
                clients.add(incomingConnection);
                

                threads.add(new ConnectionHandler(this.exam,incomingConnection,frame));
                int numConnections = this.getNumConnection();
                int numSubmit = this.getNumSubmitUser();
                frame.refeshExam(this.getNumConnection(),this.getNumSubmitUser());
            } catch (IOException ex) {
                Logger.getLogger(NetworkServer.class.getName()).log(Level.SEVERE, null, ex);
            }            
        }

        System.out.println("end openConnection");
        
        return true;
    }

    /**
     * Close all current Connection
     * @return
     */

    public boolean closeAllConnection()
    {
        try {           
           this.serverListener.close();
           int n = clients.size();
           for(int i = 0; i < n; i++)
           {
               clients.get(0).close();
               clients.remove(0);

               threads.get(0).stop = true;
               threads.remove(0);
           }
           
        }
        catch (IOException e) {
           System.out.println(e);
        }
        return true;
    }

    /**
     * get number of connection
     * @return
     */

    public int getNumConnection()
    {
        for(int i = 0; i < clients.size(); i++)
        {
            if(!clients.get(i).isConnected() || clients.get(i).isClosed())
                clients.remove(i);
        }
        return clients.size();
    }

    public int getNumSubmitUser()
    {
        int result = 0;
        for(int i = 0; i < threads.size(); i++)
        {
            if(threads.get(i).isSubmit)
                result++;
        }
        return result;
    }

    public boolean closeConnection() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public boolean print(String str)
    {        
        return true;
    }


    /**
     * @param numConnection the numConnection to set
     */
    public void setNumConnection(int numConnection) {
        this.numConnection = numConnection;
    }

    /**
     * @return the port
     */
    public int getPort() {
        return port;
    }

    /**
     * @param port the port to set
     */
    public void setPort(int port) {
        this.port = port;
    }

    public boolean startExam()
    {
        for(int i = 0 ; i < this.threads.size();  i++)
        {
            if(!threads.get(i).startExam())
                return false;
        }
        return true;
    }

    public static void main(String args[]) {
//        NetworkServer server = new NetworkServer(123);
//        server.allowConnect = true;
//        String filename = "C:\\Documents and Settings\\Vo Nguyen Khang\\Desktop\\trunk\\CS494\\Test.xml";
//        server.loadExam(filename);
//        server.openConnection(123);
//
        
        System.out.println("HELLO");
    }
    



    static class ConnectionHandler extends Thread {
     // An object of this class is a thread that will
     // process the connection with one client. The
     // thread starts itself in the constructor.


        IO.Exam exam;
        Socket connection; // A connection to the client.
        MainFrameServer frameServer;

        BufferedReader incoming;    // For receiving data from client
        PrintWriter outgoing; // For transmitting data to the client.

        public boolean isSubmit = false;



        public boolean stop = false;

        ConnectionHandler(IO.Exam exam,Socket conn,MainFrameServer frameServer) {
       // Constructor. Record the connection and
       // the directory and start the thread running.

        this.exam = exam;
        connection = conn;
        this.frameServer = frameServer;

        start();
    }

//    void sendIndex() throws Exception {
//       // This is called by the run() method in response
//       // to an "index" command. Send the list of files
//       // in the directory.
//      String[] fileList = directory.list();
//      for (int i = 0; i < fileList.length; i++)
//        outgoing.println(fileList[i]);
//      outgoing.flush();
//      outgoing.close();
//      if (outgoing.checkError())
//        throw new Exception("Error while transmitting data.");
//    }


    public void run() {
       // This is the method that is executed by the thread.
       // It creates streams for communicating with the client,
       // reads a command from the client, and carries out that
       // command. The connection is logged to standard output.
       // An output beginning with ERROR indicates that a network
       // error occurred. A line beginning with OK means that
       // there was no network error, but does not imply that the
       // command from the client was a legal command.
      
      try {
//        incoming = new TextReader( connection.getInputStream() );
        incoming = new BufferedReader (new InputStreamReader(connection.getInputStream()));
        outgoing = new PrintWriter( connection.getOutputStream() );

//        String cmd = incoming.read();


        String msg = incoming.readLine();
        System.out.println("Msg: "+msg+"?");

        while(!stop)
        {
            processMsg(msg);
            outgoing.flush();
            if(outgoing.checkError())
                throw new Exception("Error while transmitting data\n");
            msg = incoming.readLine();
            System.out.println("Msg: "+msg+"?");
        }

//        }
        System.out.println("OK " + connection.getInetAddress());
      }
      catch (Exception e) {
        System.out.println("ERROR " + connection.getInetAddress() + " " + e);
      }
      finally {
          try {
              connection.close();
              incoming.close();
              outgoing.close();
          }
          catch (IOException e) {
              System.out.println("Error: " + e.toString());
          }
      }
    }

    public void processMsg(String msg)
    {
        if(msg.equals(MSG_CLIENT_CONNECT))
        {
            System.out.println(1);
        }
        else if(msg.equals(MSG_CLIENT_DISCONNECT))
        {


            System.out.println(1);
        }
        
        else if(msg.equals(MSG_CLIENT_SUBMITTED))
        {
            try {
                String studentID = incoming.readLine();
                String studentName = incoming.readLine();
                if(saveFile(studentID))
                {
                 
                    outgoing.println(MSG_SERVER_SUBMIT_REPLY+"Y");
                    if(!this.isSubmit)
                    {
                        exam.setSubmitted(exam.getSubmitted()+1);
                        frameServer.refeshExamWhenSubmit();
                        isSubmit = true;
                    }
                    

                    System.out.println(studentID+" has submitted");
                    //this.frameServer.showDialogMessage(studentID+" has submitted");
                    this.frameServer.setLabelStatus(studentID+" has submitted");

                }
                else
                {
                    outgoing.println(MSG_SERVER_SUBMIT_REPLY+"N");
                    System.out.println(studentName+ " - "+ studentID);
                }
            } catch (IOException ex) {
                Logger.getLogger(NetworkServer.class.getName()).log(Level.SEVERE, null, ex);
            }
            

        }
        else if(msg.equals(MSG_CLIENT_REQUEST))
        {
            System.out.println(3);
            // process It -> send data to client

            outgoing.println(MSG_SERVER_REPLY);
            outgoing.flush();

            outgoing.println(exam.getTitle());            
            outgoing.println(exam.getLength());
            outgoing.println(exam.getDescription());
            outgoing.println(exam.getTotalQuestion());


            outgoing.flush();
            
            for(int i = 0 ; i < exam.getTotalQuestion(); i++)
            {
                Question question = exam.getQuestion(i);
                outgoing.println(question.getNumber().toString());
                outgoing.println(question.getStatement());
                outgoing.println(String.valueOf(question.getNumberOfChoice()));
                outgoing.flush();

                for(int j = 0; j < question.getNumberOfChoice();j++)
                {
                    outgoing.println(question.getOneChoice(j));
                    outgoing.flush();
                }
            }
        }
        else
            System.out.println("HUHU");

    }

    public boolean sendQuestion(Question question)
    {        
        outgoing.println(question.getNumber().toString());
        outgoing.println(question.getStatement());
        outgoing.println(String.valueOf(question.getNumberOfChoice()));

        

        for(int j = 0; j < question.getNumberOfChoice();j++)
        {
            outgoing.println(question.getOneChoice(j));
        }
        return true;
    }
    
    /**
     * save answer of clients to file
     * @param filename
     * @return
     */

    public boolean saveFile(String filename)
    {
        List<ClientAnswer> answers = new ArrayList<ClientAnswer>();
        String msg;
        try {
            msg = incoming.readLine();
            while(!msg.equals("__END_OF_QUESTION__"))
            {
                int questionID = Integer.parseInt(msg);
                String answer = incoming.readLine();
                answers.add(new ClientAnswer(questionID,answer));
                
                msg = incoming.readLine();
            }
        } catch (IOException ex) {
            Logger.getLogger(NetworkServer.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        IOFIle.WriteAnswersToFile(filename, answers);

        return true;
    }

    public boolean startExam()
    {
        System.out.println("Start the exam");
        outgoing.println(MSG_SERVER_START);
        outgoing.flush();
        return true;
    }

   } // end nested class ConnectionHandler
//
//    static public class ClientTimer extends Timer
//    {
//
//
//    }
//
}
    
    